using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using Server;
using Server.Network;
using Server.Engines.Craft;

namespace Server.Items
{
	[Flipable(0x14EB, 0x14EC)]
	public class MapItem : Item, ICraftable
	{
		private Rectangle2D m_Bounds;

		private int m_Width, m_Height;

		private bool m_Protected;
		private bool m_Editable;

		private List<Point2D> m_Pins = new List<Point2D>();

		private const int MaxUserPins = 50;

		[CommandProperty(AccessLevel.GameMaster)]
		public bool Protected
		{
			get { return m_Protected; }
			set { m_Protected = value; }
		}

		[CommandProperty(AccessLevel.GameMaster)]
		public Rectangle2D Bounds
		{
			get { return m_Bounds; }
			set { m_Bounds = value; }
		}

		[CommandProperty(AccessLevel.GameMaster)]
		public int Width
		{
			get { return m_Width; }
			set { m_Width = value; }
		}

		[CommandProperty(AccessLevel.GameMaster)]
		public int Height
		{
			get { return m_Height; }
			set { m_Height = value; }
		}

		public List<Point2D> Pins
		{
			get { return m_Pins; }
		}

		[Constructable]
		public MapItem()
			: base(0x14EC)
		{
			Weight = 1.0;

			m_Width = 200;
			m_Height = 200;
		}

		public virtual void CraftInit(Mobile from)
		{
		}

		public void SetDisplay(int x1, int y1, int x2, int y2, int w, int h)
		{
			Width = w;
			Height = h;

			if (x1 < 0)
				x1 = 0;

			if (y1 < 0)
				y1 = 0;

			if (x2 >= 5120)
				x2 = 5119;

			if (y2 >= 4096)
				y2 = 4095;

			Bounds = new Rectangle2D(x1, y1, x2 - x1, y2 - y1);
		}

		public MapItem(Serial serial)
			: base(serial)
		{
		}

		public override void OnDoubleClick(Mobile from)
		{
			if (from.InRange(GetWorldLocation(), 2))
				DisplayTo(from);
			else
				from.SendLocalizedMessage(500446); // That is too far away.
		}

		public virtual void DisplayTo(Mobile from)
		{
			from.Send(new MapDetails(this));
			from.Send(new MapDisplay(this));

			for (int i = 0; i < m_Pins.Count; ++i)
				from.Send(new MapAddPin(this, m_Pins[i]));

			from.Send(new MapSetEditable(this, ValidateEdit(from)));
		}

		public virtual void OnAddPin(Mobile from, int x, int y)
		{
			if (!ValidateEdit(from))
				return;
			else if (m_Pins.Count >= MaxUserPins)
				return;

			Validate(ref x, ref y);
			AddPin(x, y);
		}

		public virtual void OnRemovePin(Mobile from, int number)
		{
			if (!ValidateEdit(from))
				return;

			RemovePin(number);
		}

		public virtual void OnChangePin(Mobile from, int number, int x, int y)
		{
			if (!ValidateEdit(from))
				return;

			Validate(ref x, ref y);
			ChangePin(number, x, y);
		}

		public virtual void OnInsertPin(Mobile from, int number, int x, int y)
		{
			if (!ValidateEdit(from))
				return;
			else if (m_Pins.Count >= MaxUserPins)
				return;

			Validate(ref x, ref y);
			InsertPin(number, x, y);
		}

		public virtual void OnClearPins(Mobile from)
		{
			if (!ValidateEdit(from))
				return;

			ClearPins();
		}

		public virtual void OnToggleEditable(Mobile from)
		{
			if (Validate(from))
				m_Editable = !m_Editable;

			from.Send(new MapSetEditable(this, Validate(from) && m_Editable));
		}

		public virtual void Validate(ref int x, ref int y)
		{
			if (x < 0)
				x = 0;
			else if (x >= m_Width)
				x = m_Width - 1;

			if (y < 0)
				y = 0;
			else if (y >= m_Height)
				y = m_Height - 1;
		}

		public virtual bool ValidateEdit(Mobile from)
		{
			return m_Editable && Validate(from);
		}

		public virtual bool Validate(Mobile from)
		{
			if (!from.CanSee(this) || from.Map != this.Map || !from.Alive || InSecureTrade)
				return false;
			else if (from.AccessLevel >= AccessLevel.GameMaster)
				return true;
			else if (!Movable || m_Protected || !from.InRange(GetWorldLocation(), 2))
				return false;

			object root = RootParent;

			if (root is Mobile && root != from)
				return false;

			return true;
		}

		public void ConvertToWorld(int x, int y, out int worldX, out int worldY)
		{
			worldX = ((m_Bounds.Width * x) / Width) + m_Bounds.X;
			worldY = ((m_Bounds.Height * y) / Height) + m_Bounds.Y;
		}

		public void ConvertToMap(int x, int y, out int mapX, out int mapY)
		{
			mapX = ((x - m_Bounds.X) * Width) / m_Bounds.Width;
			mapY = ((y - m_Bounds.Y) * Width) / m_Bounds.Height;
		}

		public virtual void AddWorldPin(int x, int y)
		{
			int mapX, mapY;
			ConvertToMap(x, y, out mapX, out mapY);

			AddPin(mapX, mapY);
		}

		public virtual void AddPin(int x, int y)
		{
			m_Pins.Add(new Point2D(x, y));
		}

		public virtual void RemovePin(int index)
		{
			if (index > 0 && index < m_Pins.Count)
				m_Pins.RemoveAt(index);
		}

		public virtual void InsertPin(int index, int x, int y)
		{
			if (index < 0 || index >= m_Pins.Count)
				m_Pins.Add(new Point2D(x, y));
			else
				m_Pins.Insert(index, new Point2D(x, y));
		}

		public virtual void ChangePin(int index, int x, int y)
		{
			if (index >= 0 && index < m_Pins.Count)
				m_Pins[index] = new Point2D(x, y);
		}

		public virtual void ClearPins()
		{
			m_Pins.Clear();
		}

		public override void Serialize(GenericWriter writer)
		{
			base.Serialize(writer);

			writer.Write((int)0);

			writer.Write(m_Bounds);

			writer.Write(m_Width);
			writer.Write(m_Height);

			writer.Write(m_Protected);

			writer.Write(m_Pins.Count);
			for (int i = 0; i < m_Pins.Count; ++i)
				writer.Write(m_Pins[i]);
		}

		public override void Deserialize(GenericReader reader)
		{
			base.Deserialize(reader);

			int version = reader.ReadInt();

			switch (version)
			{
				case 0:
					{
						m_Bounds = reader.ReadRect2D();

						m_Width = reader.ReadInt();
						m_Height = reader.ReadInt();

						m_Protected = reader.ReadBool();

						int count = reader.ReadInt();
						for (int i = 0; i < count; i++)
							m_Pins.Add(reader.ReadPoint2D());

						break;
					}
			}
		}

		public static void Initialize()
		{
			PacketHandlers.Register(0x56, 11, true, new OnPacketReceive(OnMapCommand));
		}

		private static void OnMapCommand(NetState state, PacketReader pvSrc)
		{
			Mobile from = state.Mobile;
			MapItem map = World.FindItem(pvSrc.ReadInt32()) as MapItem;

			if (map == null)
				return;

			int command = pvSrc.ReadByte();
			int number = pvSrc.ReadByte();

			int x = pvSrc.ReadInt16();
			int y = pvSrc.ReadInt16();

			switch (command)
			{
				case 1: map.OnAddPin(from, x, y); break;
				case 2: map.OnInsertPin(from, number, x, y); break;
				case 3: map.OnChangePin(from, number, x, y); break;
				case 4: map.OnRemovePin(from, number); break;
				case 5: map.OnClearPins(from); break;
				case 6: map.OnToggleEditable(from); break;
			}
		}

		private sealed class MapDetails : Packet
		{
			public MapDetails(MapItem map)
				: base(0x90, 19)
			{
				m_Stream.Write((int)map.Serial);
				m_Stream.Write((short)0x139D);
				m_Stream.Write((short)map.Bounds.Start.X);
				m_Stream.Write((short)map.Bounds.Start.Y);
				m_Stream.Write((short)map.Bounds.End.X);
				m_Stream.Write((short)map.Bounds.End.Y);
				m_Stream.Write((short)map.Width);
				m_Stream.Write((short)map.Height);
			}
		}

		private abstract class MapCommand : Packet
		{
			public MapCommand(MapItem map, int command, int number, int x, int y)
				: base(0x56, 11)
			{
				m_Stream.Write((int)map.Serial);
				m_Stream.Write((byte)command);
				m_Stream.Write((byte)number);
				m_Stream.Write((short)x);
				m_Stream.Write((short)y);
			}
		}

		private sealed class MapDisplay : MapCommand
		{
			public MapDisplay(MapItem map)
				: base(map, 5, 0, 0, 0)
			{
			}
		}

		private sealed class MapAddPin : MapCommand
		{
			public MapAddPin(MapItem map, Point2D point)
				: base(map, 1, 0, point.X, point.Y)
			{
			}
		}

		private sealed class MapSetEditable : MapCommand
		{
			public MapSetEditable(MapItem map, bool editable)
				: base(map, 7, editable ? 1 : 0, 0, 0)
			{
			}
		}
		#region ICraftable Members

		public int OnCraft(int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue)
		{
			CraftInit(from);
			return 1;
		}

		#endregion
	}
}